/*
 * @Author: 唐庆明
 * @Date: 2021-09-26 15:12:04
 * @LastEditTime: 2024-07-06 22:22:30
 * @LastEditors: jiaozi
 * @Description:
 */

import * as fs from "fs-extra";
import * as path from "path";
import { AssetInfo } from "../@types/packages/asset-db/@types/public";

const util = require("util");

var expConf: any;
var compMap: any;

const projectPath = Editor.Project.path; // 工程路径

var exportPath: any; // 导出路径
var cccPath: any; // 导出路径
var ignoreFlag: any;
var delTypeStr: any;
var exportFullPath: any; // 导出补全路径
let exportFlag: any; // 导出标识
let useAutoBind: any; // 是否使用自动绑定
let replaceReg = new RegExp(`\/\/--Auto export attr, Don't change--[\\w\\W]*//--Auto export attr, Don't change end--`);
let replaceFlag = `//--Auto export attr, Don't change--\n%s\t//--Auto export attr, Don't change end--`;
let exportDelFlag = false;

let $prefabData: any = null;
let rootName = "";
let declareStr = "";
let nameList: any = {};
let sameNameList: any[] = [];

let clsPre = ""; // 生成文件名前缀
let clsSuf = ""; //生成文件名后缀
let clsName = ""; // 生成文件名和类名
let clsNamePath = ""; // 生成文件名和类名
let backNum = 0; // 往上返回几级作为文件夹名

let log = (message?: any, ...optionalParams: any[]) => {
	console.log(message, ...optionalParams);
};

class NodeTree {
	private static _inst: NodeTree = null!;
	public static getInst(): NodeTree {
		if (!NodeTree._inst) {
			NodeTree._inst = new NodeTree();
			// NodeTree._inst.init();
		}
		return NodeTree._inst;
	}

	getWarnMsg() {
		return {
			title: "Prefab导出提示",
			buttons: ["确定"],
			default: 0
		};
	}
	getAutoUIName = function (url: string) {
		return path.basename(url, path.extname(url));
	};

	/** 获取声明字符串 */
	getDeclareStr(path: string) {
		$prefabData = fs.readJsonSync(path);
		log("prefabData:", $prefabData);
		let fileData = $prefabData[0];
		if (fileData["__type__"] !== "cc.Prefab") {
			Editor.Dialog.warn(`请选中预制体文件导出`, this.getWarnMsg());
			return false;
		}
		nameList = {};
		sameNameList = [];
		declareStr = " ";
		let rootData = $prefabData[1];
		let rootName = rootData["_name"];
		this.findDeclare(rootName, rootData);
		return declareStr;
	}

	/** 递归查找需要导出节点 */
	findDeclare(rootPath: string, nodeInfo: any) {
		let name: string = nodeInfo["_name"];
		let isPrefab = false;
		// log("111", nodeInfo);
		if (!name) {
			let temp = nodeInfo;
			let fileId = "";
			let __type__ = temp["__type__"];

			// 预制体提取
			if (__type__ == "cc.Node") {
				let childID = temp["_prefab"]["__id__"];
				temp = $prefabData[childID];

				// log("222", temp);
				if (temp) {
					__type__ = temp["__type__"];

					if (__type__ == "cc.PrefabInfo") {
						isPrefab = true;
						fileId = temp.fileId;
						let childID = temp["instance"]["__id__"];
						temp = $prefabData[childID];
						// log("333", temp);
						if (temp) {
							__type__ = temp["__type__"];
							if (__type__ == "cc.PrefabInstance") {
								let propertyOverrides = temp["propertyOverrides"];
								if (propertyOverrides) {
									for (let i = 0; i < propertyOverrides.length; i++) {
										const d = propertyOverrides[i];

										let childID = d["__id__"];
										temp = $prefabData[childID];
										// log("555", temp);
										if (temp) {
											__type__ = temp["__type__"];
											if (__type__ == "CCPropertyOverrideInfo") {
												if (temp["propertyPath"][0] == "_name") {
													// 判断是不是自己这个节点的名称
													let targetInfo = $prefabData[temp.targetInfo.__id__];
													// log("666", targetInfo, targetInfo.localID[0] == fileId);
													// log("777", targetInfo.localID[0], fileId);
													if (targetInfo.localID[0] == fileId) {
														name = temp["value"];
														// log("propertyPath name:", name);
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}

			if (!name) {
				return;
			}
		}

		// log("findDeclare name:", name, isPrefab);

		let path = `${rootPath}/${name}`;
		// （导出标识为空 || 存在导出标识） && 不是默认取名

		if (name.indexOf(ignoreFlag) === 0) {
			return;
		}

		if (name != rootName && (exportFlag == "" || name.indexOf(exportFlag) === 0) && name.indexOf(" ") == -1) {
			let noFlagName = name.slice(exportFlag.length, name.length).replace(/-/g, "_");

			// log("noFlagName:", noFlagName);
			if (nameList[name] == undefined) {
				//同名控件检查
				nameList[name] = true;
				let type = "Node";
				for (let k in compMap) {
					let reg = `^${k}`;
					// log("noFlagName name:", noFlagName, k);
					if (noFlagName.match(new RegExp(reg))) {
						type = compMap[k];
						break;
					}
				}
				// log("noFlagName name:", noFlagName, type);
				let _type = type.replace(new RegExp(delTypeStr, "g"), "");
				declareStr += `\t@property({ type: ${_type}, group: { id: "0", name: "自动绑定节点" } })\n\t${
					exportDelFlag == true ? "" : exportFlag
				}${noFlagName}: ${_type} = null!;\n`;
			} else {
				sameNameList.push(path);
			}
		}

		if (isPrefab) {
			return;
		}

		let children = nodeInfo["_children"];
		if (!children) return; // 无子节点

		for (const childInfo in children) {
			const element = children[childInfo];
			let childID = element["__id__"];
			let childNode = $prefabData[childID];
			this.findDeclare(path, childNode);
		}
	}

	/** 根据模板文件导出 */
	async exportFileByTsTemp(str: string, uiName: string, filePath: string) {
		// log("filePath:", str, filePath, uiName);
		let reStr = util.format(replaceFlag, str);
		let scriptPath = await Editor.Message.request("asset-db", "query-path", filePath);
		log("scriptPath:", scriptPath);
		if (scriptPath && fs.existsSync(scriptPath)) {
			// 如果文件已存在，就替换自动导出部分的内容
			let scriptTemplate = fs.readFileSync(scriptPath!) + "";
			scriptTemplate = scriptTemplate.replace(replaceReg, reStr);

			await Editor.Message.request("asset-db", "save-asset", filePath, scriptTemplate);
			return false;
		} else {
			// 不存在就按模版文件创建
			let tempName = useAutoBind ? "tsTempAutoBind.txt" : "tsTemp.txt";
			log("tempName:", tempName);
			let templatePath = path.join(__dirname, `../static/template/${tempName}`).replace(/\\/g, "/"); //await Editor.Message.request("asset-db", "query-path", `packages://template/${tempName}`);
			log("templatePath:", templatePath);
			let scriptTemplate = fs.readFileSync(templatePath!) + "";

			let dirPath = "";
			for (let i = 0; i < backNum; i++) {
				dirPath += "../";
			}

			scriptTemplate = scriptTemplate.replace(/_CLASS_NAME_PATH_/g, clsNamePath);
			scriptTemplate = scriptTemplate.replace(/_CLASS_NAME_/g, clsName);
			scriptTemplate = scriptTemplate.replace(replaceReg, reStr);
			await Editor.Message.request("asset-db", "create-asset", filePath, scriptTemplate);
			return true;
		}
	}

	// 遍历设置属性
	async setProperty(node: any, comPath: string, prefabUUid: string) {
		let name = node.name;

		// log("节点数据:", node);
		if (!name) {
			return;
		}
		//if (name == "$btn_wishlist") {
		// log("节点名称:", name, comPath, prefabUUid);
		//	}
		// log(node.uuid, node.components);

		if (name != rootName && (exportFlag == "" || name.indexOf(exportFlag) === 0) && name.indexOf(" ") == -1) {
			let noFlagName = name.slice(exportFlag.length, name.length).replace(/-/g, "_");
			//同名控件检查
			nameList[name] = true;
			let type = "cc.Node";
			for (let k in compMap) {
				let reg = `^${k}`;
				if (noFlagName.match(new RegExp(reg))) {
					type = compMap[k];
					break;
				}
			}

			let uuid = node.uuid;
			if (type != "Node") {
				for (let i = 0; i < node.components.length; i++) {
					const com = node.components[i];
					if (com.type == type) {
						uuid = com.value;
						break;
					}
				}
			}
			let path = `${comPath}.${exportDelFlag == true ? "" : exportFlag}${noFlagName}`;
			// log("exportDelFlag:", exportDelFlag, exportDelFlag == true ? "" : exportFlag, path);
			let param = {
				uuid: prefabUUid,
				path: path,
				dump: {
					type: type,
					value: {
						uuid: uuid
					}
				}
			};
			// log("节点信息:", param);
			await Editor.Message.request("scene", "set-property", param);
		}

		let prefab = node.prefab;
		if (prefab && prefab.state == 1) {
			let children = node.children;
			if (!children) return; // 无子节点
			for (const cd of children) {
				await this.setProperty(cd, comPath, prefabUUid);
			}
		}
	}

	init() {}

	initCfg() {
		let cfgPath = path.join(__dirname, `../static/template/export-cfg.json`).replace(/\\/g, "/");
		let rawdata = fs.readFileSync(cfgPath, { encoding: "utf-8" });
		let cfg = JSON.parse(rawdata);

		log("配置表数据:", cfg);

		expConf = cfg.port;
		compMap = cfg.compMap;

		exportPath = expConf.exportPath;
		cccPath = expConf.cccPath;
		exportFullPath = path.join(projectPath, "assets", exportPath).replace(/\\/g, "/");
		exportFlag = expConf.exportFlag;
		useAutoBind = expConf.useAutoBind;
		ignoreFlag = expConf.ignoreFlag;
		delTypeStr = expConf.delTypeStr;
		clsPre = expConf.clsPre;
		clsSuf = expConf.clsSuf;
		clsName = "";
		exportDelFlag = expConf.exportDelFlag;
		backNum = expConf.backNum;
	}

	async dealPrefab(assetInfo: AssetInfo) {
		this.initCfg();

		clsName = "";
		if (!fs.existsSync(exportFullPath)) {
			fs.mkdirsSync(exportFullPath);
		}
		let url = assetInfo.url;
		log("assetInfo:", assetInfo);
		let folder = path.dirname(url);
		//获取预制体所在文件夹名称
		let dirNames = folder.split("/");
		let dirName = "";

		let cccStr = folder.replace(cccPath, "");
		let fDt = 0;
		for (var i = 0; i < cccStr.length; i++) {
			// 遍历字符串
			// str.charAt(i)返回str中索引为i的字符
			var chars = cccStr.charAt(i);
			if (chars == "/") {
				fDt++;
			}
		}

		if (fDt) {
			backNum = fDt;
		}

		if (backNum > 0) {
			//dirName = dirNames[dirNames.length - backNum];
			for (let i = backNum; i >= 1; i--) {
				dirName += dirNames[dirNames.length - i] + "/";
			}
		}

		log("folder", folder);
		log("cccStr", cccStr, backNum);
		log("dirName", dirName);

		let uiName = this.getAutoUIName(url);
		//生成对应的ts文件
		clsName = `${clsPre}${uiName.slice(0, 1).toUpperCase() + uiName.slice(1)}${clsSuf}`;
		log("导出路径:", folder, uiName, clsName);
		let exportUIPath = "";
		clsNamePath = dirName + clsName;
		if (exportPath === "") {
			exportUIPath = `${folder}/${clsName}.ts`;
		} else {
			if (dirName) {
				exportUIPath = `db://assets/${exportPath}/${dirName}${clsName}.ts`;
			} else {
				exportUIPath = `db://assets/${exportPath}/${clsName}.ts`;
			}
		}

		exportUIPath = exportUIPath.replace(/\\/g, "/");
		log("exportUIPath:", exportUIPath);
		log("exportPath:", exportPath);
		let strData = this.getDeclareStr(assetInfo.file);
		if (sameNameList.length > 0) {
			let warn = sameNameList.join("\n");
			log("export warn - same name::" + warn);
			Editor.Dialog.warn(`有命名重复请修改:` + sameNameList.toString(), this.getWarnMsg());
			return;
		}

		let isNewCreate = false;
		if (strData) {
			isNewCreate = await this.exportFileByTsTemp(strData, uiName, exportUIPath);
		}

		log(`开始添加脚本`);
		Editor.Message.send("scene", "snapshot");

		// 1.检测原文件是否存在
		let fileUuid = await Editor.Message.request("asset-db", "query-uuid", exportUIPath);
		log("fileUuid:", fileUuid);
		let compressUUID = Editor.Utils.UUID.compressUUID(fileUuid!, false);
		log("compressUUID:", compressUUID);
		let uuid = assetInfo.uuid;
		log("uuid:", uuid);

		// let type = Editor.Selection.getLastSelectedType();
		let ids = Editor.Selection.getSelected("node");

		let prefabUUid = ids[0];
		log("prefabUUid:", prefabUUid);
		if (prefabUUid) {
			// 2.检测类名
			// scriptName = await Editor.Message.request('scene','query-script-name', fileUuid);
			// 3.检测组件类名是否存在

			// log("type:", type);
			let q_node = await Editor.Message.request("scene", "query-node-tree", prefabUUid);
			// log("ids:", ids, clsName, q_node);
			let comps = q_node.components;
			let comPath = "";
			for (let index = comps.length - 1; index >= 0; index--) {
				const com = comps[index];
				// log("com.type:", com.type, clsName, com.type == clsName);
				if (com.type == clsName) {
					comPath = `__comps__.${index}`;
					break;
				}
			}

			log(`comPath: ${comPath}`);

			let callback1 = async (val?: any) => {
				Editor.Message.send("scene", "snapshot");
				q_node = await Editor.Message.request("scene", "query-node-tree", prefabUUid);
				await this.setProperty(q_node, comPath, prefabUUid);
			};

			let callback2 = async (val?: any) => {
				await Editor.Message.request("scene", "create-component", {
					uuid: prefabUUid,
					component: clsName
				});

				comPath = `__comps__.${comps.length}`;
				await callback1();
			};

			if (!comPath) {
				if (isNewCreate) {
					let callback = async (val: any) => {
						// log("新建类导入成功回调:", val);
						Editor.Message.removeBroadcastListener("programming:compiled", callback);
						await callback2();
						log(`export prefab0: ${uiName} ==> ${exportUIPath}`);
					};

					Editor.Message.addBroadcastListener("programming:compiled", callback);
				} else {
					await callback2();
					log(`export prefab1: ${uiName} ==> ${exportUIPath}`);
				}
			} else {
				await callback1();
				log(`export prefab2: ${uiName} ==> ${exportUIPath}`);
			}
		}
	}

	getDate = () => {
		let date = new Date();
		let d = date.toLocaleDateString();
		let hour = date.getHours();
		let min = date.getMinutes();
		let sec = date.getSeconds();
		let h = hour < 10 ? "0" + hour : hour;
		let m = min < 10 ? "0" + min : min;
		let s = sec < 10 ? "0" + sec : sec;
		return `${d}  ${h}:${m}:${s}`;
	};
}

export let nodeTree = NodeTree.getInst();
